package com.h2cloud.JUCTest.CountDownLatch;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.h2cloud.JUCTest.TimeUtil.Sleeper.sleep;

/**
 * @Package: com.h2cloud.JUCTest.CountDownLatch
 * @ClassName: CountDownLatchTest
 * @Author: h2cloud
 * @CreateTime: 2022/6/6 3:43 下午
 * @Description:
 */
@Slf4j(topic = "c.CountDownLatch")
public class CountDownLatchTest {

    public static void main(String[] args) throws InterruptedException {
//        CountDownLatch countDownLatch = new CountDownLatch(3);
//        method1();
//        // method2();
//        Random random = new Random();
//        int i = random.nextInt(100);
//        System.out.println("i = " + i);
//        method3();

        CountDownLatchTest test = new CountDownLatchTest();
        test.method4();
    }

    public static void method1() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(3);

        new Thread(() -> {
            log.info("t1 start ...");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("t1 end ...");
            countDownLatch.countDown();
        }, "t1").start();


        new Thread(() -> {
            log.info("t2 start ...");

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("t2 end ...");
            countDownLatch.countDown();
        }, "t2").start();

        new Thread(() -> {
            log.info("t3 start ...");

            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("t3 end ...");
            countDownLatch.countDown();
        }, "t3").start();


        log.info("main wait ...");
        countDownLatch.await();
        log.info("main wait end ...");
    }

    public static void method2() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(3);

        ExecutorService executorService = Executors.newFixedThreadPool(4);
        executorService.submit(() -> {
            log.info("t1 start ...");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
            log.info("t1 end ...{}", countDownLatch.getCount());
        });

        executorService.submit(() -> {
            log.info("t2 start ...");

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("t2 end ...{}", countDownLatch.getCount());
            countDownLatch.countDown();
        });

        executorService.submit(() -> {
            log.info("t3 start ...");

            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("t3 end ...{}", countDownLatch.getCount());
            countDownLatch.countDown();
        });

        executorService.submit(() -> {
            log.info("main wait ...");
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("main wait end ...");
            executorService.shutdown();
        });
    }

    public static void method3() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(10);
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        String[] all = new String[10];
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int id = i;
            executorService.submit(() -> {
                for (int j = 0; j <= 100; j++) {
                    try {
                        Thread.sleep(random.nextInt(100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    all[id] = j + "%";
                    System.out.print("\r" + Arrays.toString(all));
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        System.out.println();
        System.out.println("游戏开始");
        executorService.shutdown();
    }


    // 写道题：实现一个容器，提供两个方法，add，size
    // 写两个线程，线程1添加10个元素到容器中，线程2实现监控元素的个数，
    // 当个数到5个时，线程2给出提示并结束?
    // 使用 CountDownLatch 代替wait notify 好处。
    public void method4() {
        Container c = new Container();
        new Thread(() -> {
            int idx = 1;
            while (true) {
                try {
                    c.set(idx);
                    System.out.println("t1 set " + idx);
                    idx++;
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            while (true) {
                final List<Integer> list;
                try {
                    list = c.get();
                    System.out.println("t2 list = " + list);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    class Container {
        List<Integer> dataList = new ArrayList<>();
        CountDownLatch emptyCountDownLatch = new CountDownLatch(5);
        CountDownLatch fullCountDownLatch = new CountDownLatch(1);

        public Container() {
        }

        public void set(int val) throws InterruptedException {
            if (dataList.size() == 5) {
                fullCountDownLatch.await();
                fullCountDownLatch = new CountDownLatch(1);
            }
            dataList.add(val);
            emptyCountDownLatch.countDown();
        }

        public List<Integer> get() throws InterruptedException {
            emptyCountDownLatch.await();
            emptyCountDownLatch = new CountDownLatch(5);
            List<Integer> res = new ArrayList<>(dataList);
            dataList.clear();
            fullCountDownLatch.countDown();
            return res;
        }
    }


}


